/**
 * @file rtk_timer.c
 * @author LokLiang (lokliang@163.com)
 * @brief
 * @version 0.1
 * @date 2023-05-01
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "prv_rtk_class.h"

static void _rtk_timer_q_thread(void *arg);
static void _rtk_timer_q_resume(void *arg);

static void _rtk_timer_q_thread(void *arg)
{
    _RTK_UNUSE(arg);

    for (;;)
    {
        for (;;)
        {
            size_t sched_nest = rtk_sched_suspend();
            if (curr_thread_handle->obj_enable == 0)
            {
                rtk_thread_suspend();
                rtk_sched_resume(sched_nest);
            }
            else
            {
                rtk_sched_resume(sched_nest);
                break;
            }
        }

        rtk_tick_t timeout = k_timer_q_handler(&curr_thread_handle->obj.timer_q_handle);

        if ((cm_rtk->nest_sched | cm_rtk->nest_int) == 0)
        {
            rtk_thread_sleep(timeout);
        }
    }
}

static void _rtk_timer_q_resume(void *arg)
{
    _rtk_list_atomic_resume(arg);
}

rtk_err_t rtk_timer_q_open(size_t stack_size, rtk_prior_t priority)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    size_t sched_nest = rtk_sched_suspend();

    rtk_err_t ret;

    do
    {
        if (cm_rtk->thread_timer.hdl == NULL)
        {
            rtk_thread_t *thread_handle = rtk_thread_create(NULL,                // rtk_thread_t *thread_handle,
                                                            "sys-timer",         // const char *name,
                                                            _rtk_timer_q_thread, // void (*entry)(void *arg),
                                                            NULL,                // void *arg,
                                                            stack_size,          // size_t stack_size,
                                                            priority,            // rtk_prior_t priority,
                                                            0                    // u8_t slice_ticks
            );
            if (thread_handle == NULL)
            {
                ret = RTK_NOMEM;
                break;
            }

            struct rtk_thread_handle *thread = thread_handle->hdl;
            default_timer_q_hdl = &thread->obj.timer_q_handle;
            if (k_timer_q_create(&thread->obj.timer_q_handle) != 0)
            {
                rtk_thread_delete(thread_handle);
                ret = RTK_NOMEM;
                break;
            }

            k_timer_q_resume_regist(&thread->obj.timer_q_handle, _rtk_timer_q_resume, thread);

            cm_rtk->thread_timer.hdl = thread_handle->hdl;
            thread->flag |= _RTK_THREAD_FLAG_SYS;

            rtk_timer_q_start();
            ret = RTK_OK;
        }
        else
        {
            _RTK_WRN("thread already started");
            ret = RTK_FAIL;
        }

    } while (0);

    rtk_sched_resume(sched_nest);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_q_close(void)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    size_t sched_nest = rtk_sched_suspend();

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl)
    {
        struct rtk_thread_handle *thread = cm_rtk->thread_timer.hdl;

        k_timer_q_delete(&thread->obj.timer_q_handle);
        rtk_thread_delete((rtk_thread_t *)&thread);
        cm_rtk->thread_timer.hdl = NULL;

        ret = RTK_OK;
    }
    else
    {
        _RTK_WRN("thread already closed");
        ret = RTK_FAIL;
    }

    rtk_sched_resume(sched_nest);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_q_start(void)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl)
    {
        struct rtk_thread_handle *thread = cm_rtk->thread_timer.hdl;
        thread->obj_enable = 1;
        rtk_thread_resume((rtk_thread_t *)&thread);
        ret = RTK_OK;
    }
    else
    {
        _RTK_WRN("thread never start");
        ret = RTK_FAIL;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_q_stop(void)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl)
    {
        struct rtk_thread_handle *thread = cm_rtk->thread_timer.hdl;
        thread->obj_enable = 0;
        rtk_thread_set_suspend((rtk_thread_t *)&thread);
        ret = RTK_OK;
    }
    else
    {
        _RTK_WRN("thread never start");
        ret = RTK_FAIL;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_create(rtk_timer_t *timer_handle, rtk_timer_fn timer_route, void *arg, u8_t sub_prior)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    rtk_err_t ret;

    do
    {
        if (cm_rtk->thread_timer.hdl == NULL)
        {
            _RTK_WRN("timer service never started");
            ret = RTK_FAIL;
            break;
        }
        struct rtk_thread_handle *thread = cm_rtk->thread_timer.hdl;
        if (k_timer_create((k_timer_t *)timer_handle, &thread->obj.timer_q_handle, (k_timer_fn)timer_route, arg, sub_prior) == 0)
        {
            ret = RTK_OK;
        }
        else
        {
            ret = RTK_NOMEM;
        }

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_delete(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_ASS_FALSE(rtk_timer_is_valid(timer_handle) == false, "");

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl == NULL)
    {
        _RTK_WRN("timer service never started");
        ret = RTK_FAIL;
    }
    else
    {
        k_timer_delete((k_timer_t *)timer_handle);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_set_period(rtk_timer_t *timer_handle, bool periodic, rtk_tick_t period)
{
    _RTK_TRACE_ENTRY();
    _RTK_ASS_FALSE(rtk_timer_is_valid(timer_handle) == false, "");

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl == NULL)
    {
        _RTK_WRN("timer service never started");
        ret = RTK_FAIL;
    }
    else
    {
        k_timer_set_period((k_timer_t *)timer_handle, periodic, period);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_start(rtk_timer_t *timer_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_ASS_FALSE(rtk_timer_is_valid(timer_handle) == false, "");

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl == NULL)
    {
        _RTK_WRN("timer service never started");
        ret = RTK_FAIL;
    }
    {
        k_timer_start((k_timer_t *)timer_handle, delay_ticks);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_timer_stop(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_ASS_FALSE(rtk_timer_is_valid(timer_handle) == false, "");

    rtk_err_t ret;

    if (cm_rtk->thread_timer.hdl == NULL)
    {
        _RTK_WRN("timer service never started");
        ret = RTK_FAIL;
    }
    else
    {
        k_timer_stop((k_timer_t *)timer_handle);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_timer_is_periodic(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = k_timer_is_periodic((k_timer_t *)timer_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_timer_is_pending(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = k_timer_is_pending((k_timer_t *)timer_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_timer_is_valid(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = k_timer_is_valid((k_timer_t *)timer_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_tick_t rtk_timer_get_period(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    rtk_tick_t ret = k_timer_get_period((k_timer_t *)timer_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_tick_t rtk_timer_get_remain(rtk_timer_t *timer_handle)
{
    _RTK_TRACE_ENTRY();
    rtk_tick_t ret = k_timer_time_remain((k_timer_t *)timer_handle);
    _RTK_TRACE_EXIT();
    return ret;
}
